"""
Folder Management Service
文件夹管理服务 - 文件夹CRUD和层级管理
"""

import uuid
from typing import Optional, Dict, Any, List
from datetime import datetime

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func, update
from sqlalchemy.orm import selectinload
import structlog

from ..models.user import User
from ..models.folder import Folder, FolderType, FolderVisibility
from ..models.generated_content import GeneratedContent
from ..core.database import DatabaseSession
from ..core.config import settings
from ..core.exceptions import (
    FolderNotFoundError,
    FolderAccessDeniedError,
    FolderNameConflictError,
    ValidationError,
    PermissionDeniedError,
)

# 配置结构化日志
logger = structlog.get_logger(__name__)


class FolderManagementService:
    """文件夹管理服务类"""

    def __init__(self, db_session: DatabaseSession):
        self.db_session = db_session

    async def create_folder(
        self,
        user: User,
        name: str,
        parent_id: Optional[uuid.UUID] = None,
        description: Optional[str] = None,
        folder_type: str = FolderType.USER,
        visibility: str = FolderVisibility.PRIVATE,
        color: Optional[str] = None,
        icon: Optional[str] = None,
        **kwargs
    ) -> Folder:
        """
        创建文件夹

        Args:
            user: 用户对象
            name: 文件夹名称
            parent_id: 父文件夹ID
            description: 描述
            folder_type: 文件夹类型
            visibility: 可见性
            color: 颜色
            icon: 图标
            **kwargs: 其他参数

        Returns:
            Folder: 创建的文件夹

        Raises:
            ValidationError: 参数验证失败
            FolderNameConflictError: 名称冲突
            FolderNotFoundError: 父文件夹不存在
        """
        logger.info("创建文件夹", user_id=str(user.id), name=name, parent_id=str(parent_id) if parent_id else None)

        # 验证参数
        await self._validate_folder_params(name, description, folder_type, visibility)

        async with self.db_session as session:
            # 检查父文件夹
            parent_folder = None
            if parent_id:
                parent_result = await session.execute(
                    select(Folder).where(Folder.id == parent_id)
                )
                parent_folder = parent_result.scalar_one_or_none()
                
                if not parent_folder:
                    raise FolderNotFoundError(f"父文件夹 {parent_id} 不存在")
                
                # 检查父文件夹权限
                if parent_folder.user_id != user.id and not user.is_admin:
                    raise FolderAccessDeniedError("无权在指定父文件夹中创建文件夹")

            # 检查名称冲突（在同一父文件夹下）
            conflict_query = select(Folder).where(
                and_(
                    Folder.user_id == user.id,
                    Folder.name == name,
                    Folder.parent_id == parent_id,
                    Folder.is_deleted == False
                )
            )
            conflict_result = await session.execute(conflict_query)
            if conflict_result.scalar_one_or_none():
                raise FolderNameConflictError(f"文件夹名称 '{name}' 已存在")

            # 构建路径
            path = self._build_folder_path(name, parent_folder)
            level = self._calculate_folder_level(parent_folder)

            # 创建文件夹
            folder = Folder(
                user_id=user.id,
                parent_id=parent_id,
                name=name,
                description=description,
                folder_type=folder_type,
                path=path,
                level=level,
                visibility=visibility,
                color=color,
                icon=icon,
                **kwargs
            )

            session.add(folder)
            await session.flush()

            # 更新父文件夹的子文件夹计数
            if parent_folder:
                parent_folder.update_subfolder_count(1)

            await session.commit()
            await session.refresh(folder)

            logger.info("文件夹创建成功", folder_id=str(folder.id), name=name)
            return folder

    async def get_folder(self, folder_id: uuid.UUID, user: User) -> Optional[Folder]:
        """
        获取文件夹

        Args:
            folder_id: 文件夹ID
            user: 用户对象

        Returns:
            Optional[Folder]: 文件夹对象，不存在返回None

        Raises:
            FolderAccessDeniedError: 无权访问
        """
        async with self.db_session as session:
            result = await session.execute(
                select(Folder)
                .where(Folder.id == folder_id)
                .options(
                    selectinload(Folder.user),
                    selectinload(Folder.parent),
                    selectinload(Folder.children)
                )
            )
            folder = result.scalar_one_or_none()

            if not folder:
                return None

            # 检查权限
            if folder.user_id != user.id and not user.is_admin:
                # 检查是否为公开或共享文件夹
                if folder.visibility not in [FolderVisibility.PUBLIC, FolderVisibility.SHARED]:
                    raise FolderAccessDeniedError("无权访问此文件夹")

            # 更新最后访问时间
            folder.mark_as_accessed()
            await session.commit()

            return folder

    async def get_user_folders(
        self,
        user: User,
        parent_id: Optional[uuid.UUID] = None,
        folder_type: Optional[str] = None,
        visibility: Optional[str] = None,
        include_deleted: bool = False,
        skip: int = 0,
        limit: int = 100
    ) -> List[Folder]:
        """
        获取用户的文件夹列表

        Args:
            user: 用户对象
            parent_id: 父文件夹ID过滤
            folder_type: 文件夹类型过滤
            visibility: 可见性过滤
            include_deleted: 是否包含已删除的文件夹
            skip: 跳过数量
            limit: 限制数量

        Returns:
            List[Folder]: 文件夹列表
        """
        async with self.db_session as session:
            query = select(Folder).where(Folder.user_id == user.id)

            # 应用过滤条件
            if parent_id is not None:
                query = query.where(Folder.parent_id == parent_id)
            if folder_type:
                query = query.where(Folder.folder_type == folder_type)
            if visibility:
                query = query.where(Folder.visibility == visibility)
            if not include_deleted:
                query = query.where(Folder.is_deleted == False)

            # 排序
            query = query.order_by(Folder.sort_order, Folder.name.asc())

            # 分页
            query = query.offset(skip).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def get_folder_tree(
        self,
        user: User,
        root_folder_id: Optional[uuid.UUID] = None
    ) -> List[Dict[str, Any]]:
        """
        获取文件夹树结构

        Args:
            user: 用户对象
            root_folder_id: 根文件夹ID，如果为None则获取所有文件夹

        Returns:
            List[Dict[str, Any]]: 文件夹树
        """
        async with self.db_session as session:
            if root_folder_id:
                # 获取指定根文件夹及其子文件夹
                root_folder = await self.get_folder(root_folder_id, user)
                if not root_folder:
                    return []
                
                folders = [root_folder] + root_folder.get_descendants(include_self=False)
            else:
                # 获取所有文件夹
                query = select(Folder).where(
                    and_(
                        Folder.user_id == user.id,
                        Folder.is_deleted == False
                    )
                ).options(selectinload(Folder.children))
                result = await session.execute(query)
                all_folders = result.scalars().all()
                
                # 只获取根文件夹（没有父文件夹的）
                folders = [f for f in all_folders if f.is_root]

            # 构建树结构
            return [self._build_folder_tree_dict(folder) for folder in folders]

    async def update_folder(
        self,
        folder_id: uuid.UUID,
        user: User,
        name: Optional[str] = None,
        description: Optional[str] = None,
        visibility: Optional[str] = None,
        color: Optional[str] = None,
        icon: Optional[str] = None,
        **kwargs
    ) -> Folder:
        """
        更新文件夹

        Args:
            folder_id: 文件夹ID
            user: 用户对象
            name: 名称
            description: 描述
            visibility: 可见性
            color: 颜色
            icon: 图标
            **kwargs: 其他参数

        Returns:
            Folder: 更新后的文件夹

        Raises:
            FolderNotFoundError: 文件夹不存在
            FolderAccessDeniedError: 无权操作
            FolderNameConflictError: 名称冲突
            ValidationError: 参数验证失败
        """
        logger.info("更新文件夹", folder_id=str(folder_id), user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(Folder)
                .where(Folder.id == folder_id)
                .options(selectinload(Folder.parent))
            )
            folder = result.scalar_one_or_none()

            if not folder:
                raise FolderNotFoundError(f"文件夹 {folder_id} 不存在")

            # 检查权限
            if folder.user_id != user.id and not user.is_admin:
                raise FolderAccessDeniedError("无权更新此文件夹")

            # 检查是否可以更新
            if not folder.can_rename:
                raise ValidationError("此文件夹无法重命名")

            # 验证参数
            await self._validate_folder_params(name, description, folder.visibility, folder.folder_type)

            # 检查名称冲突（在同一父文件夹下）
            if name and name != folder.name:
                conflict_query = select(Folder).where(
                    and_(
                        Folder.user_id == user.id,
                        Folder.name == name,
                        Folder.parent_id == folder.parent_id,
                        Folder.id != folder_id,
                        Folder.is_deleted == False
                    )
                )
                conflict_result = await session.execute(conflict_query)
                if conflict_result.scalar_one_or_none():
                    raise FolderNameConflictError(f"文件夹名称 '{name}' 已存在")

            # 更新字段
            if name is not None:
                folder.name = name
            if description is not None:
                folder.description = description
            if visibility is not None:
                folder.visibility = visibility
            if color is not None:
                folder.color = color
            if icon is not None:
                folder.icon = icon

            # 更新其他字段
            for key, value in kwargs.items():
                if hasattr(folder, key):
                    setattr(folder, key, value)

            await session.commit()
            await session.refresh(folder)

            logger.info("文件夹更新成功", folder_id=str(folder_id))
            return folder

    async def delete_folder(self, folder_id: uuid.UUID, user: User) -> bool:
        """
        删除文件夹（软删除）

        Args:
            folder_id: 文件夹ID
            user: 用户对象

        Returns:
            bool: 是否成功删除

        Raises:
            FolderNotFoundError: 文件夹不存在
            FolderAccessDeniedError: 无权操作
            ValidationError: 文件夹无法删除
        """
        logger.info("删除文件夹", folder_id=str(folder_id), user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(Folder)
                .where(Folder.id == folder_id)
                .options(selectinload(Folder.children))
            )
            folder = result.scalar_one_or_none()

            if not folder:
                raise FolderNotFoundError(f"文件夹 {folder_id} 不存在")

            # 检查权限
            if folder.user_id != user.id and not user.is_admin:
                raise FolderAccessDeniedError("无权删除此文件夹")

            # 检查是否可以删除
            if not folder.can_delete:
                raise ValidationError("此文件夹无法删除")

            # 检查是否有子文件夹
            if folder.children:
                raise ValidationError("文件夹包含子文件夹，无法删除")

            # 检查是否有内容
            content_result = await session.execute(
                select(func.count(GeneratedContent.id))
                .where(GeneratedContent.folder_id == folder_id)
            )
            content_count = content_result.scalar()
            
            if content_count > 0:
                raise ValidationError(f"文件夹包含 {content_count} 个内容，无法删除")

            # 软删除
            folder.mark_as_deleted()
            await session.commit()

            logger.info("文件夹删除成功", folder_id=str(folder_id))
            return True

    async def move_folder(
        self,
        folder_id: uuid.UUID,
        new_parent_id: Optional[uuid.UUID],
        user: User
    ) -> Folder:
        """
        移动文件夹

        Args:
            folder_id: 文件夹ID
            new_parent_id: 新父文件夹ID（None表示移动到根目录）
            user: 用户对象

        Returns:
            Folder: 移动后的文件夹

        Raises:
            FolderNotFoundError: 文件夹不存在
            FolderAccessDeniedError: 无权操作
            ValidationError: 无法移动到指定位置
        """
        logger.info("移动文件夹", 
                   folder_id=str(folder_id), 
                   new_parent_id=str(new_parent_id) if new_parent_id else None,
                   user_id=str(user.id))

        async with self.db_session as session:
            # 获取要移动的文件夹
            result = await session.execute(
                select(Folder)
                .where(Folder.id == folder_id)
                .options(selectinload(Folder.parent), selectinload(Folder.children))
            )
            folder = result.scalar_one_or_none()

            if not folder:
                raise FolderNotFoundError(f"文件夹 {folder_id} 不存在")

            # 检查权限
            if folder.user_id != user.id and not user.is_admin:
                raise FolderAccessDeniedError("无权移动此文件夹")

            # 检查目标父文件夹
            new_parent_folder = None
            if new_parent_id:
                parent_result = await session.execute(
                    select(Folder).where(Folder.id == new_parent_id)
                )
                new_parent_folder = parent_result.scalar_one_or_none()
                
                if not new_parent_folder:
                    raise FolderNotFoundError(f"目标父文件夹 {new_parent_id} 不存在")
                
                if new_parent_folder.user_id != user.id and not user.is_admin:
                    raise FolderAccessDeniedError("无权移动到指定父文件夹")

                # 检查是否尝试移动到自身或子文件夹
                if self._is_descendant(folder, new_parent_folder):
                    raise ValidationError("不能将文件夹移动到自身或其子文件夹中")

            # 检查名称冲突
            if new_parent_folder:
                conflict_query = select(Folder).where(
                    and_(
                        Folder.user_id == user.id,
                        Folder.name == folder.name,
                        Folder.parent_id == new_parent_id,
                        Folder.id != folder_id,
                        Folder.is_deleted == False
                    )
                )
                conflict_result = await session.execute(conflict_query)
                if conflict_result.scalar_one_or_none():
                    raise FolderNameConflictError(f"文件夹名称 '{folder.name}' 在目标位置已存在")

            # 更新父文件夹引用计数
            if folder.parent:
                folder.parent.update_subfolder_count(-1)
            if new_parent_folder:
                new_parent_folder.update_subfolder_count(1)

            # 更新文件夹的父ID和路径
            folder.parent_id = new_parent_id
            folder.path = self._build_folder_path(folder.name, new_parent_folder)
            folder.level = self._calculate_folder_level(new_parent_folder)

            await session.commit()
            await session.refresh(folder)

            logger.info("文件夹移动成功", folder_id=str(folder_id))
            return folder

    async def rename_folder(
        self,
        folder_id: uuid.UUID,
        new_name: str,
        user: User
    ) -> Folder:
        """
        重命名文件夹

        Args:
            folder_id: 文件夹ID
            new_name: 新名称
            user: 用户对象

        Returns:
            Folder: 重命名后的文件夹

        Raises:
            FolderNotFoundError: 文件夹不存在
            FolderAccessDeniedError: 无权操作
            FolderNameConflictError: 名称冲突
            ValidationError: 名称无效
        """
        logger.info("重命名文件夹", folder_id=str(folder_id), new_name=new_name, user_id=str(user.id))

        async with self.db_session as session:
            result = await session.execute(
                select(Folder)
                .where(Folder.id == folder_id)
                .options(selectinload(Folder.parent))
            )
            folder = result.scalar_one_or_none()

            if not folder:
                raise FolderNotFoundError(f"文件夹 {folder_id} 不存在")

            # 检查权限
            if folder.user_id != user.id and not user.is_admin:
                raise FolderAccessDeniedError("无权重命名此文件夹")

            # 检查是否可以重命名
            if not folder.can_rename:
                raise ValidationError("此文件夹无法重命名")

            # 重命名文件夹
            success = folder.rename(new_name)
            if not success:
                raise ValidationError("文件夹重命名失败")

            await session.commit()
            await session.refresh(folder)

            logger.info("文件夹重命名成功", folder_id=str(folder_id), new_name=new_name)
            return folder

    async def get_folder_contents(
        self,
        folder_id: uuid.UUID,
        user: User,
        content_type: Optional[str] = None,
        skip: int = 0,
        limit: int = 50
    ) -> List[GeneratedContent]:
        """
        获取文件夹中的内容

        Args:
            folder_id: 文件夹ID
            user: 用户对象
            content_type: 内容类型过滤
            skip: 跳过数量
            limit: 限制数量

        Returns:
            List[GeneratedContent]: 内容列表

        Raises:
            FolderNotFoundError: 文件夹不存在
            FolderAccessDeniedError: 无权访问
        """
        async with self.db_session as session:
            folder = await self.get_folder(folder_id, user)
            if not folder:
                raise FolderNotFoundError(f"文件夹 {folder_id} 不存在")

            query = select(GeneratedContent).where(
                and_(
                    GeneratedContent.folder_id == folder_id,
                    GeneratedContent.user_id == user.id
                )
            )

            # 应用过滤条件
            if content_type:
                query = query.where(GeneratedContent.content_type == content_type)

            # 排序和分页
            query = query.order_by(GeneratedContent.created_at.desc()).offset(skip).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def get_folder_statistics(self, user: User) -> Dict[str, Any]:
        """
        获取用户文件夹统计

        Args:
            user: 用户对象

        Returns:
            Dict[str, Any]: 统计信息
        """
        async with self.db_session as session:
            # 总体统计
            total_result = await session.execute(
                select(
                    func.count(Folder.id),
                    func.count(Folder.id).filter(Folder.folder_type == FolderType.USER),
                    func.count(Folder.id).filter(Folder.folder_type == FolderType.SYSTEM),
                    func.count(Folder.id).filter(Folder.is_deleted == True),
                    func.count(Folder.id).filter(Folder.visibility == FolderVisibility.PUBLIC),
                    func.count(Folder.id).filter(Folder.visibility == FolderVisibility.SHARED)
                ).where(Folder.user_id == user.id)
            )
            total, user_folders, system_folders, deleted_folders, public_folders, shared_folders = total_result.first()

            # 按层级统计
            by_level_result = await session.execute(
                select(
                    Folder.level,
                    func.count(Folder.id)
                )
                .where(
                    and_(
                        Folder.user_id == user.id,
                        Folder.is_deleted == False
                    )
                )
                .group_by(Folder.level)
                .order_by(Folder.level)
            )
            by_level = dict(by_level_result.all())

            return {
                "user_id": str(user.id),
                "total_folders": total or 0,
                "user_folders": user_folders or 0,
                "system_folders": system_folders or 0,
                "deleted_folders": deleted_folders or 0,
                "public_folders": public_folders or 0,
                "shared_folders": shared_folders or 0,
                "by_level": by_level,
                "max_level": max(by_level.keys()) if by_level else 0
            }

    async def cleanup_deleted_folders(self, max_age_days: int = 30) -> int:
        """
        清理已删除的文件夹（永久删除）

        Args:
            max_age_days: 最大保留天数

        Returns:
            int: 清理的文件夹数量
        """
        cutoff_date = datetime.utcnow() - timedelta(days=max_age_days)

        async with self.db_session as session:
            result = await session.execute(
                select(Folder)
                .where(
                    and_(
                        Folder.is_deleted == True,
                        Folder.deleted_at < cutoff_date
                    )
                )
            )
            deleted_folders = result.scalars().all()

            # 永久删除文件夹
            for folder in deleted_folders:
                await session.delete(folder)

            await session.commit()

            cleaned_count = len(deleted_folders)
            logger.info("清理已删除文件夹完成", cleaned_count=cleaned_count, max_age_days=max_age_days)
            return cleaned_count

    # 私有辅助方法

    async def _validate_folder_params(
        self,
        name: Optional[str],
        description: Optional[str],
        folder_type: str,
        visibility: str
    ) -> None:
        """验证文件夹参数"""
        if name is not None:
            if not name or len(name.strip()) == 0:
                raise ValidationError("文件夹名称不能为空")
            if len(name) > 100:
                raise ValidationError("文件夹名称过长，最大100字符")
            if not self._is_valid_folder_name(name):
                raise ValidationError("文件夹名称包含非法字符")

        if description is not None and len(description) > 500:
            raise ValidationError("文件夹描述过长，最大500字符")

        if folder_type not in [FolderType.USER, FolderType.SYSTEM, FolderType.SHARED, FolderType.ARCHIVE]:
            raise ValidationError(f"无效的文件夹类型: {folder_type}")

        if visibility not in [FolderVisibility.PRIVATE, FolderVisibility.PUBLIC, FolderVisibility.SHARED]:
            raise ValidationError(f"无效的可见性设置: {visibility}")

    def _is_valid_folder_name(self, name: str) -> bool:
        """验证文件夹名称是否有效"""
        # 检查是否包含非法字符
        invalid_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']
        return not any(char in name for char in invalid_chars)

    def _build_folder_path(self, name: str, parent_folder: Optional[Folder]) -> str:
        """构建文件夹路径"""
        if parent_folder:
            return f"{parent_folder.path}/{name}"
        return name

    def _calculate_folder_level(self, parent_folder: Optional[Folder]) -> int:
        """计算文件夹层级"""
        return (parent_folder.level + 1) if parent_folder else 0

    def _is_descendant(self, folder: Folder, potential_ancestor: Folder) -> bool:
        """检查是否为后代文件夹"""
        current = folder.parent
        while current:
            if current.id == potential_ancestor.id:
                return True
            current = current.parent
        return False